#include <EventLoop.h>
#include <stdlib.h>
struct  EventLoop* eventLoopInit() {
    return eventLoopInitEx(NULL);
}

struct EventLoop* eventLoopInitEx(const char* threadName) {
    struct EventLoop* evLoop = (struct EventLoop*) malloc(sizeof (struct EventLoop));
    evLoop->isQuit = false;
    evLoop->threadID = pthread_self();
    pthread_mutex_init(&evLoop->mutex, NULL);
    strcpy(evLoop->threadName, threadName == NULL ? "MainThread" : threadName);
    evLoop->dispatcher = &EpollDispatcher;
    evLoop->dispatcherData = evLoop->dispatcher->init();
    evLoop->head = evLoop->tail = NULL;
    evLoop->channelMap = channelMapInit(128);
    if(-1 == socketpair(AF_UNIX, SOCK_STREAM, 0, evLoop->socketPair)) {
        perror("socketPair");
        exit(0);
    }

    struct Channel* channel = channelInit(evLoop->socketPair[1], ReadEvent,
            readLocalMessage, NULL, NULL, evLoop);
    eventLoopAddTask(evLoop, channel, ADD);

    return evLoop;
}

int eventLoopRun(struct EventLoop* evLoop) {
    if(!evLoop) {
        return -1;
    }

    struct Dispatcher* dispatcher = evLoop->dispatcher;

    if(evLoop->threadID != pthread_self()) {
        return -1;
    }

    while (!evLoop->isQuit) {
        dispatcher->dispatch(evLoop, 2);
        eventLoopProcessTask(evLoop);
    }
    return 0;
}

int eventActivate(struct EventLoop* evLoop, int fd, int event) {
    if(fd < 0 || evLoop == NULL) {
        return -1;
    }

    struct Channel* channel = evLoop->channelMap->list[fd];
    if(channel->fd != fd) {
        return -1;
    }

    if((event & ReadEvent) && channel->readCallback) {
        channel->readCallback(channel->arg);
    }

    if((event & WriteEvent) && channel->writeCallback) {
        channel->writeCallback(channel->arg);
    }
    return 0;
}

int eventLoopAddTask(struct EventLoop* evLoop, struct Channel* channel, int type) {
    pthread_mutex_lock(&evLoop->mutex);
    struct ChannelElement* node = (struct ChannelElement*)malloc(sizeof(struct ChannelElement));
    node->channel = channel;
    node->type = type;
    node->next = NULL;
    if(evLoop->head == NULL) {
        evLoop->head = evLoop->tail = node;
    } else {
        evLoop->tail->next = node;
        evLoop->tail = node;
    }
    pthread_mutex_unlock(&evLoop->mutex);

    if(evLoop->threadID == pthread_self()) {
        eventLoopProcessTask(evLoop);
    } else {
        taskWakeup(evLoop);
    }

    return 0;
}

int eventLoopProcessTask(struct EventLoop* evLoop) {
    pthread_mutex_lock(&evLoop->mutex);
    struct ChannelElement* head = evLoop->head;
    while(head != NULL) {
        struct Channel * channel = head->channel;
        if(head->type == ADD) {
            eventLoopAdd(evLoop, channel);
        } else if(head->type == DELETE) {
            eventLoopRemove(evLoop, channel);
        } else if(head->type == MODIFY) {
            eventLoopModify(evLoop, channel);
        }
        struct ChannelElement* tmp = head;
        head = head->next;
        free(tmp);
    }
    evLoop->head = evLoop->tail = NULL;
    pthread_mutex_unlock(&evLoop->mutex);
    return 0;
}

int eventLoopAdd(struct EventLoop* evLoop, struct Channel* channel) {
    int fd = channel->fd;
    struct ChannelMap * channelMap = evLoop->channelMap;
    if(fd >= channelMap->size) {
        if(!makeMapRoom(channelMap, fd, sizeof(struct Channel*))) {
            return -1;
        }
    }

    if(channelMap->list[fd] == NULL) {
        channelMap->list[fd] = channel;
        evLoop->dispatcher->add(channel, evLoop);
    }
    return 0;
}

int eventLoopRemove(struct EventLoop* evLoop, struct Channel* channel) {
    int fd = channel->fd;
    struct ChannelMap* channelMap = evLoop->channelMap;
    if(fd >= channelMap->size) {
        return -1;
    }
    return evLoop->dispatcher->remove(channel, evLoop);
}

int eventLoopModify(struct EventLoop* evLoop, struct Channel* channel) {
    int fd = channel->fd;
    struct ChannelMap* channelMap = evLoop->channelMap;
    if(channelMap->list[fd] != NULL) {
        return -1;
    }
    return evLoop->dispatcher->modify(channel, evLoop);
}

void taskWakeup(struct EventLoop* evLoop) {
    const char* msg = "hello_world!";
    write(evLoop->socketPair[0], msg, strlen(msg));
}

int readLocalMessage(void* arg) {
    struct EventLoop* evLoop = (struct EventLoop*)arg;
    char buf[256] = {0};
    read(evLoop->socketPair[1], buf, sizeof(buf));
    return 0;
}

int destroyChannel(struct EventLoop* evLoop, struct Channel* channel) {
    evLoop->channelMap->list[channel->fd] = NULL;
    close(channel->fd);
    free(channel);
    return 0;
}
